#include "MainFrame.h"

#include <core/Events.h>
#include <core/FileSys.h>
#include <core/StyleMgr.h>
#include <core/Utility.h>
#include <include/NameDefines.h>
#include <uam/UserAccountManager.h>
#include <widgets/KeySearchWidget.h>
#include <widgets/SBaseDialog.h>
#include <widgets/SImageButton.h>
#include <widgets/WidgetResizer.h>
#include <widgets/cefview/BrowserDialog.h>
#include <widgets/cefview/CefViewWidget.h>

#include <QApplication>
#include <QBoxLayout>
#include <QJsonArray>
#include <QJsonObject>
#include <QJsonValue>
#include <QMouseEvent>
#include <QPushButton>
#include <QStackedWidget>
#include <QStylePainter>
#include <QTimer>
#include <QUrl>

#include "../CMD/CmdMgr.h"
#include "../CMD/Command.h"
#include "../ModuleMgr.h"
#include "StatusBar.h"
#include "TabBar.h"
#include "TitleBar.h"

MainFrame::MainFrame() : QWidget(nullptr) {
  setWindowFlag(Qt::FramelessWindowHint);
  setFocusPolicy(Qt::StrongFocus);
  setMinimumSize(1020, 700);

  QVBoxLayout* mainLayout = new QVBoxLayout();
  mainLayout->setContentsMargins(1, 1, 1, 1);
  mainLayout->setSpacing(1);

  QVBoxLayout* topLayout = new QVBoxLayout();
  topLayout->setContentsMargins(0, 0, 0, 0);
  topLayout->setSpacing(0);

  m_title = new TitleBar(this);
  m_title->setFixedHeight(76);
  topLayout->addWidget(m_title);
  connect(this, &MainFrame::setCommonPage, m_title, &TitleBar::onCommonPage);

  m_relatedCmdBar = new QWidget(this);
  m_relatedCmdBar->setFixedHeight(26 * m_uiScale);
  QPalette palette = m_relatedCmdBar->palette();
  palette.setColor(QPalette::Window, QColor(0x080F0F));
  m_relatedCmdBar->setPalette(palette);
  m_relatedCmdBar->setAutoFillBackground(true);
  m_relatedCmdBar->hide();

  topLayout->addWidget(m_relatedCmdBar);
  mainLayout->addLayout(topLayout);

  m_contentWidget = new QStackedWidget(this);
  m_contentWidget->setSizePolicy(QSizePolicy::Expanding,
                                 QSizePolicy::Expanding);
  mainLayout->addWidget(m_contentWidget);
  this->setLayout(mainLayout);

  m_resizer = new WidgetResizer(this);

  connect(m_tabBar, &TabBar::tabClose, this, &MainFrame::onTabClose);
  connect(m_tabBar, &TabBar::tabActivated, this, &MainFrame::onTabActivated);
  connect(m_tabBar, &TabBar::tabSeperated, this, &MainFrame::onTabSeperated);

  qApp->installEventFilter(this);

  initializeCmdRelations();

  QTimer::singleShot(0, this, &MainFrame::openHome);
}

void MainFrame::paintEvent(QPaintEvent* event) {
  QStylePainter painter(this);
  painter.scale(m_uiScale, m_uiScale);
  painter.fillRect(rect(), NAME_COLOR("BasicBackground"));
}

bool MainFrame::eventFilter(QObject* watched, QEvent* event) {
  if (event->type() == QEvent::KeyPress) {
    QWidget* foucsW = qApp->focusWidget();
    if (foucsW == nullptr || foucsW == this ||
        watched == m_title->searchWidget()) {
      m_title->showSearch();
      m_title->searchWidget()->appendSearchText(
          (static_cast<QKeyEvent*>(event))->text());
      m_title->searchWidget()->setFocus();
      return true;
    }
  } else if (event->type() == QEvent::Move) {
    normalNoArgEventDispatcher.dispatch(EVENT_MAINWINDOW_MOVE);
  }
  return false;
}

int MainFrame::existCmdWindow(int cmdId, const QString& params,
                              bool enableParam) {
  int winId = -1;
  for (auto cmdWin : m_cmdWindows) {
    if (cmdWin.cmdId == cmdId && cmdWin.widget != nullptr &&
        (!enableParam ||
         cmdWin.params.compare(params, Qt::CaseInsensitive) == 0)) {
      winId = cmdWin.winId;
      break;
    }
  }

  return winId;
}

int MainFrame::openCmdWindow(int cmdId, const QString& params, QWidget* widget,
                             const QString& title, bool popup) {
  int winId = -1;
  for (auto& cmdWin : m_cmdWindows) {
    if (cmdWin.cmdId == cmdId &&
        cmdWin.params.compare(params, Qt::CaseInsensitive) == 0) {
      winId = cmdWin.winId;
      cmdWin.widget = widget;
      CmdWindowInfo* cwi = m_tabBar->tabById(winId);
      if (cwi) cwi->widget = widget;
      if (cmdWin.isPopup())
        cmdWin.widget->activateWindow();
      else
        m_tabBar->activateTabByTabId(cmdWin.winId);

      onTabActivated(winId);
      return winId;
    }
  }
  if (!popup) {
    // TODO: "fixed" as parameter
    CmdWindowInfo& ti = m_tabBar->addTab(title, widget, cmdId, cmdId == 20044);
    ti.params = params;
    m_cmdWindows[ti.winId] = ti;
    winId = ti.winId;
  } else {
    CmdWindowInfo ti;
    ti.cmdId = cmdId;
    ti.title = title;
    ti.winId = CmdMgr::instance().cmdWindowId();
    ti.setPopup(true);

    SBaseDialog* dlg = dynamic_cast<SBaseDialog*>(widget);
    if (dlg == nullptr) {
      dlg = new SBaseDialog(SBaseDialog::WindowType::kDialog);
      dlg->setContentWidget(widget);
      dlg->show();
    }
    ti.widget = widget;
    m_cmdWindows[ti.winId] = ti;
    winId = ti.winId;
  }

  onTabActivated(winId);

  return winId;
}

void MainFrame::activateCmdWindow(int tabId) {
  if (m_tabBar->tabById(tabId))
    m_tabBar->activateTabByTabId(tabId);
  else {
    auto cw = m_cmdWindows.value(tabId);
    if (cw.widget) cw.widget->activateWindow();
  }
}

void MainFrame::updateWindowTitle(int windowId, const QString& title) {
  if (m_tabBar->tabById(windowId)) {
    m_tabBar->updateTabTitle(windowId, title);
  } else {
    auto cw = m_cmdWindows.value(windowId);
    if (cw.widget) cw.widget->setWindowTitle(title);
  }
}

// SLOTS:
void MainFrame::onTabClose(int tabId) {
  CmdWindowInfo* ti = m_tabBar->tabById(tabId);
  if (!ti) return;

  closeWindow(tabId);
}

void MainFrame::onTabActivated(int newId) {
  m_currentTabId = newId;
  CmdWindowInfo* newTi = m_tabBar->tabById(newId);
  if (!newTi) return;

  newTi->setPopup(false);

  QWidget* newWidget = newTi ? newTi->widget : nullptr;
  if (newWidget != m_curContentChild) {
    m_curContentChild = newWidget;

    if (m_curContentChild) {
      int index = m_contentWidget->indexOf(m_curContentChild);
      if (index == -1) index = m_contentWidget->addWidget(m_curContentChild);
      m_contentWidget->setCurrentIndex(index);
      m_curContentChild->show();
    }
  }

  updateRelationBar(newTi ? newTi->cmdId : -1);

#if USE_QB
  ::SetFocus(nullptr);
  setFocus();
#endif
}

void MainFrame::onTabSeperated(CmdWindowInfo ti) {
  m_cmdWindows[ti.winId].setPopup(true);
  m_contentWidget->removeWidget(ti.widget);

  SBaseDialog* dlg = dynamic_cast<SBaseDialog*>(ti.widget);
  if (dlg == nullptr) {
    dlg = new SBaseDialog(SBaseDialog::WindowType::kDialog);
    dlg->setContentWidget(ti.widget);
    ti.widget->show();
  }

  if (dlg) {
    dlg->detach();
    dlg->move(QCursor::pos() - QPoint(100, 10));
    dlg->setTitle(ti.title);
    dlg->show();
    connect(dlg, &SBaseDialog::moving, this, &MainFrame::onPopupMoving);
    connect(dlg, &SBaseDialog::finished, this, &MainFrame::onPoupuClosed);
  }
}

void MainFrame::onPopupMoving(SBaseDialog* dlg) {
  QPoint mousePos = QCursor::pos();
  mousePos = m_tabBar->mapFromGlobal(mousePos);
  if (m_tabBar->rect().contains(mousePos)) {
    for (auto& ti : m_cmdWindows) {
      if (dynamic_cast<SBaseDialog*>(ti.widget) == dlg) {
        dlg->setWindowType(SBaseDialog::WindowType::kChild);
      } else if (ti.widget == dlg->contentWidget()) {
        dlg->removeContentWidget();
        dlg->deleteLater();
      } else
        continue;

      m_tabBar->attachWidget(ti);  // will block until drag end!
      break;
    }
  }
}

void MainFrame::onPoupuClosed() {
  SBaseDialog* sbd = dynamic_cast<SBaseDialog*>(sender());
  if (!sbd) return;
  QWidget* widget = dynamic_cast<QWidget*>(sbd);
  for (auto iter = m_cmdWindows.begin(); iter != m_cmdWindows.end(); ++iter) {
    if (iter->widget == widget || iter->widget == sbd->contentWidget()) {
      closeWindow(iter.key());
      return;
    }
  }
}

void MainFrame::onRelationCmdClick() {
  QString name = sender()->objectName();
  int cmdId = name.toInt();
  CmdMgr::instance().execCmd(cmdId);
}

void MainFrame::openHome() {
  CmdMgr::instance().execCmd(20044);

  {
    const QString& pushbtn_qss =
        QString(
            "QPushButton{background-color:#%1; color:#%2;} "
            "QPushButton:hover{background-color:#%3;}")
            .arg(StyleMgr::instance().color("MainTitleBack").rgb(), 0, 16)
            .arg(StyleMgr::instance().color("LabelNormal").rgb(), 0, 16)
            .arg(StyleMgr::instance().color("Hover").rgb(), 0, 16);
    {
      QPushButton* customer_service =
          new QPushButton(tr("客服"), m_title->statusBar());
      customer_service->setObjectName("Customer Service");
      customer_service->setStyleSheet(pushbtn_qss);
      customer_service->setFixedSize(32, 24);
      m_title->statusBar()->addBtn(customer_service, nullptr);
      connect(customer_service, &QPushButton::clicked, this,
              &MainFrame::onServiceClicked);
    }

    {
      QPushButton* help_center =
          new QPushButton(tr("帮助中心"), m_title->statusBar());
      help_center->setStyleSheet(pushbtn_qss);
      help_center->setFixedSize(56, 24);
      m_title->statusBar()->addBtn(help_center, nullptr);
      connect(help_center, &QPushButton::clicked, this,
              &MainFrame::onHelpClicked);
    }
  }
}

void MainFrame::onServiceClicked() {
  QString url = CmdMgr::instance().LoadURL("QB_WEB_CUSTOMSERVICE");
  if (url.isEmpty()) return;

  // append accountinfo to the url
  {
    const CUserAccountInfo& uinfo =
        SSUserAccountManager::instance().GetUserInfo();
    url += QString(
               "{\"qmName\":\"%1\",\"qmOrg\":\"%2\",\"qbAccount\":\"%3\","
               "\"Tel\":\"%4\"}&partnerid=%5")
               .arg(QString::fromUtf8(QUrl::toPercentEncoding(
                   QString::fromLocal8Bit(uinfo.m_DisplayName))))
               .arg(QString::fromUtf8(QUrl::toPercentEncoding(
                   QString::fromLocal8Bit(uinfo.m_CompanyName))))
               .arg(uinfo.m_UserAccount)
               .arg(uinfo.m_Mobile)
               .arg(uinfo.m_UserId);
  }

  if (m_sevcDlg) {  // already popup
    m_sevcDlg->raise();
    m_sevcDlg->show();
    return;
  }

  m_sevcDlg = CefViewWidget::createPopup(url, nullptr);
  QRect rect(0, 0, 510, 500);
  if (screen()) {
    rect.moveCenter(screen()->geometry().center());
  }
  m_sevcDlg->setGeometry(rect);
  m_sevcDlg->show();
}

void MainFrame::onHelpClicked() {
  QString url = CmdMgr::instance().LoadURL("HELPCENTER_SPECIFICPAGE");
  if (url.isEmpty()) return;

  CmdWindowInfo* newTi = m_tabBar->tabById(m_currentTabId);
  if (!newTi) return;
  const int currentCmdId = newTi->cmdId;

  // fill url with current tab id
  url = QString::asprintf(url.toStdString().c_str(), currentCmdId);

  if (m_helpDlg) {  // already popup
    m_helpDlg->raise();
    m_helpDlg->Navigate(url);
    m_helpDlg->show();
    return;
  }

  m_helpDlg = CefViewWidget::createPopup(url, nullptr);
  QRect rect(0, 0, 900, 680);  // = screen()->geometry();
  if (screen()) {
    rect.moveCenter(screen()->geometry().center());
  }
  m_helpDlg->setGeometry(rect);
  m_helpDlg->show();
}

void MainFrame::initializeCmdRelations() {
  QJsonDocument doc = CoreUtility::openJsonDoc(
      FileSys::instance()->appCfgDir() + "/menuRelation.json");
  if (doc.isEmpty()) return;

  QJsonArray arr = doc.array();
  for (auto v : arr) {
    QJsonObject obj = v.toObject();
    QJsonArray ra = obj["relation"].toArray();
    QVector<int> relations;
    for (auto rv : ra) {
      int cmdId = rv.toInt();
      const Command* cmd = CmdMgr::instance().command(cmdId);
      if (!cmd || cmd->module().isEmpty()) continue;
      relations.push_back(rv.toInt());
    }
    if (!relations.isEmpty()) m_relationCmds.push_back(relations);
  }
}

void MainFrame::updateRelationBar(int activeCmd) {
  // 当已存在时，仅更新stylesheet
  if (m_curRelations.contains(activeCmd)) {
    QString style;
    StyleMgr::StyleType clrType = StyleMgr::instance().getColorStyle();
    if (StyleMgr::StyleType::kBlue == clrType) {
      style =
          "QPushButton{background:#232628; "
          "border-radius:0px;color:#2CB5A4}\nQPushButton:hover{background:#"
          "232628;text-decoration: underline;color: #2CB5A4}\n"
          "#%1{background:#232628}";
    } else {
      style =
          "QPushButton{background:#121A19; "
          "border-radius:0px;color:#FFE5C7}\nQPushButton:hover{background:#"
          "172422}\n"
          "#%1{background:#0C4F44}";
    }

    style = style.arg(QString::number(activeCmd));
    m_relatedCmdBar->setStyleSheet(style);
    m_relatedCmdBar->show();
    return;
  }

  QHBoxLayout* l = dynamic_cast<QHBoxLayout*>(m_relatedCmdBar->layout());
  if (l) {
    QLayoutItem* child;
    while ((child = l->takeAt(0)) != nullptr) {
      delete child->widget();  // delete the widget
      delete child;            // delete the layout item
    }
  } else {
    l = new QHBoxLayout();
    l->setContentsMargins(0, 1 * m_uiScale, 0, 0);
    l->setSpacing(1 * m_uiScale);
    m_relatedCmdBar->setLayout(l);
  }

  m_curRelations.clear();
  for (auto rs : m_relationCmds) {
    for (auto r : rs) {
      if (r == activeCmd) {
        m_curRelations = rs;
        break;
      }
    }
  }

  if (m_curRelations.isEmpty()) {
    m_relatedCmdBar->hide();
    return;
  }

  for (auto cmdId : m_curRelations) {
    Command* cmd = CmdMgr::instance().command(cmdId);
    if (!cmd) continue;
    QPushButton* btn = new QPushButton(cmd->name());
    btn->setObjectName(QString::number(cmdId));
    btn->setFixedSize(80 * m_uiScale, 25 * m_uiScale);
    l->addWidget(btn);

    connect(btn, &QPushButton::clicked, this, &MainFrame::onRelationCmdClick);
    connect(this, &MainFrame::scaleChanged, btn, [btn](float scale) {
      btn->setFixedSize(80 * scale, 25 * scale);
      btn->resize(80 * scale, 25 * scale);
      QFont font = btn->font();
      font.setPixelSize(12 * scale);
      btn->setFont(font);
    });
  }
  QPushButton* btn = new QPushButton();
  btn->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
  btn->setFixedHeight(25 * m_uiScale);
  connect(this, &MainFrame::scaleChanged, btn, [btn](float scale) {
    btn->setFixedHeight(25 * scale);
    btn->resize(1, 25 * scale);
  });
  btn->setDisabled(true);
  l->addWidget(btn);

  QString style;
  StyleMgr::StyleType clrType = StyleMgr::instance().getColorStyle();
  if (StyleMgr::StyleType::kBlue == clrType) {
    style =
        "QPushButton{background:#232628; "
        "border-radius:0px;color:#2CB5A4}\nQPushButton:hover{background:#"
        "232628;text-decoration: underline;color: #2CB5A4}\n"
        "#%1{background:#232628}";
  } else {
    style =
        "QPushButton{background:#121A19; "
        "border-radius:0px;color:#FFE5C7}\nQPushButton:hover{background:#"
        "172422}\n"
        "#%1{background:#0C4F44}";
  }

  style = style.arg(QString::number(activeCmd));
  m_relatedCmdBar->setStyleSheet(style);
  m_relatedCmdBar->show();
}

void MainFrame::closeWindow(int wid) {
  if (!m_cmdWindows.contains(wid)) return;

  CmdWindowInfo ti = m_cmdWindows[wid];
  m_cmdWindows.remove(wid);
  bool stillUsed = false;
  for (auto cmdWin : m_cmdWindows) {
    if (cmdWin.widget == ti.widget) {
      stillUsed = true;  // but if we support dragout, this seems impossible!
      break;
    }
  }

  if (!stillUsed)  // the widgets is not used any more
  {
    m_contentWidget->removeWidget(ti.widget);
    if (m_curContentChild == ti.widget) m_curContentChild = nullptr;

    Command* cmd = CmdMgr::instance().command(ti.cmdId);
    if (cmd) {
      IModule* module = ModuleMgr::instance().module(cmd->module());
      if (module) module->closeCommandWindow(ti.cmdId, ti.widget);
    }
  }
}

void MainFrame::onScaleChanged(float scale) {
  // Example!!! Please improve it!!!
  m_uiScale = scale;
  QFont font = QApplication::font();
  font.setPixelSize(12 * m_uiScale);  // point size may more accurate,and shoud
                                      // use the original font size!
  QApplication::setFont(font);

  // layout()->setContentsMargins(0 * m_uiScale, 0 * m_uiScale, 0 * m_uiScale, 0
  // * m_uiScale); layout()->setSpacing(1 * m_uiScale);

  m_relatedCmdBar->setFixedHeight(26 * m_uiScale);
  m_relatedCmdBar->resize(m_relatedCmdBar->width() * m_uiScale, 26 * m_uiScale);

  if (m_relatedCmdBar->layout()) {
    m_relatedCmdBar->layout()->setContentsMargins(0, 1 * m_uiScale, 0, 0);
    m_relatedCmdBar->layout()->setSpacing(1 * m_uiScale);
  }

  scaleChanged(m_uiScale);
  updateGeometry();

  QWidgetList wl = QApplication::allWidgets();
  for (QWidget* w : wl) {
    QApplication::postEvent(
        w, new QEvent((QEvent::Type)SSEventType::UserScaleChanged));
  }
}

void MainFrame::setCommonPageNotify(int* ids, int size, int ntype) {
  setCommonPage(ids, size, ntype);
}

void MainFrame::setIasMenuNotify(const QString& iasmenu) {
  CmdMgr::instance().setIasMenu(iasmenu);
}

void MainFrame::addStatusbarItem(QWidget* btn, QWidget* before /*= nullptr*/) {
  m_title->statusBar()->addBtn(btn, before);
}

KeySearchWidget* MainFrame::searchWidget() { return m_title->searchWidget(); }

QWidget* MainFrame::cmdWindow(int tabid) {
  CmdWindowInfo* cwi = m_tabBar->tabById(tabid);
  if (cwi) return cwi->widget;

  return nullptr;
}

void MainFrame::openAccountInfo(const QString& tag) {
  // 此处的实现为侵入式编程，方案欠妥。原因是AccountInfo面板的触发逻辑全部被封装在auth模块中，并且未对外提供访问接口
  QPushButton* btn = m_title->statusBar()->findChild<QPushButton*>(
      kAccountInfo, Qt::FindDirectChildrenOnly);
  ;
  if (btn) {
    btn->setObjectName(QString(kAccountInfo) + tag);
    emit btn->clicked();
    btn->setObjectName(QString(kAccountInfo));
  }
}
